import sys
import os
import re
from PyQt5.QtWidgets import (QApplication, QMainWindow, QWidget, QVBoxLayout, QHBoxLayout,
                             QLabel, QTextEdit, QPushButton, QFileDialog, QMessageBox,
                             QGroupBox, QCheckBox, QSplitter, QScrollArea)
from PyQt5.QtCore import Qt

import difflib  # 添加difflib库

class PatchApplicator(QMainWindow):
    def __init__(self):
        super().__init__()
        # 先初始化属性
        self.original_content = ""
        self.patch_content = ""
        self.modified_content = ""
        self.parsed_hunks = []
        self.located_positions = []
        self.match_leading_whitespace = True  # 控制行首空格/Tab是否参与匹配（默认精确匹配）
        # 再初始化UI
        self.initUI()
        
    def initUI(self):
        # 保持原有UI初始化代码不变...
        self.setWindowTitle('打补丁程序 - 增强版块解析')
        self.setGeometry(100, 100, 1400, 900)
        
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QVBoxLayout(central_widget)
        
        # 文件选择区域
        file_group = QGroupBox("文件选择")
        file_layout = QHBoxLayout(file_group)
        
        self.source_btn = QPushButton("选择源代码文件")
        self.source_btn.clicked.connect(self.select_source_file)
        file_layout.addWidget(self.source_btn)
        
        self.patch_btn = QPushButton("选择补丁文件")
        self.patch_btn.clicked.connect(self.select_patch_file)
        file_layout.addWidget(self.patch_btn)
        
        main_layout.addWidget(file_group)
        
        # 处理步骤区域（保持不变）
        steps_group = QGroupBox("处理步骤")
        steps_layout = QHBoxLayout(steps_group)
        
        self.parse_btn = QPushButton("1. 解析补丁")
        self.parse_btn.clicked.connect(self.parse_patch)
        steps_layout.addWidget(self.parse_btn)
        
        self.locate_btn = QPushButton("2. 定位补丁")
        self.locate_btn.clicked.connect(self.locate_patch)
        steps_layout.addWidget(self.locate_btn)
        
        self.apply_btn = QPushButton("3. 应用补丁")
        self.apply_btn.clicked.connect(self.apply_patch)
        steps_layout.addWidget(self.apply_btn)
        
        self.verify_btn = QPushButton("验证补丁")
        self.verify_btn.clicked.connect(self.verify_patch)
        steps_layout.addWidget(self.verify_btn)
        
        self.save_btn = QPushButton("保存修改")
        self.save_btn.clicked.connect(self.save_changes)
        steps_layout.addWidget(self.save_btn)

        # 在处理步骤区域添加一个"生成差异"按钮
        self.diff_btn = QPushButton("生成差异")
        self.diff_btn.clicked.connect(self.generate_diff)
        steps_layout.addWidget(self.diff_btn)
        
        main_layout.addWidget(steps_group)

        
        # 内容显示区域（保持不变）
        content_splitter = QSplitter(Qt.Horizontal)
        
        source_group = QGroupBox("源代码")
        source_layout = QVBoxLayout(source_group)
        self.source_text = QTextEdit()
        self.source_text.setReadOnly(True)
        source_layout.addWidget(self.source_text)
        
        patch_group = QGroupBox("补丁内容")
        patch_layout = QVBoxLayout(patch_group)
        self.patch_text = QTextEdit()
        self.patch_text.setReadOnly(True)
        patch_layout.addWidget(self.patch_text)
        
        modified_group = QGroupBox("修改后代码")
        modified_layout = QVBoxLayout(modified_group)
        self.modified_text = QTextEdit()
        self.modified_text.setReadOnly(True)
        modified_layout.addWidget(self.modified_text)
        
        content_splitter.addWidget(source_group)
        content_splitter.addWidget(patch_group)
        content_splitter.addWidget(modified_group)
        content_splitter.setSizes([400, 300, 400])
        
        main_layout.addWidget(content_splitter, 1)
        
        # 调试信息区域
        debug_group = QGroupBox("调试信息")
        debug_layout = QVBoxLayout(debug_group)
        self.debug_text = QTextEdit()
        self.debug_text.setReadOnly(True)
        debug_layout.addWidget(self.debug_text)
        
        main_layout.addWidget(debug_group)
        
        # 选项
        options_group = QGroupBox("选项")
        options_layout = QHBoxLayout(options_group)
        self.verify_checkbox = QCheckBox("应用后验证补丁")
        self.verify_checkbox.setChecked(True)
        options_layout.addWidget(self.verify_checkbox)
        # 新增：行首空格/Tab匹配选项
        self.whitespace_checkbox = QCheckBox("匹配行首空格/Tab")
        self.whitespace_checkbox.setChecked(True)
        self.whitespace_checkbox.stateChanged.connect(
            lambda: setattr(self, "match_leading_whitespace", self.whitespace_checkbox.isChecked())
        )
        options_layout.addWidget(self.whitespace_checkbox)  # 将复选框加入布局
        options_layout.addStretch()
        
        main_layout.addWidget(options_group)
        
        self.update_button_states()
    
    def update_button_states(self):
        """根据当前状态更新按钮的可用性"""
        has_source = bool(self.original_content)
        has_patch = bool(self.patch_content)
        has_parsed = bool(self.parsed_hunks)
        has_located = bool(self.located_positions) and all(pos is not None for pos in self.located_positions)
        has_modified = bool(self.modified_content)
        
        self.parse_btn.setEnabled(has_source and has_patch)
        self.locate_btn.setEnabled(has_parsed)
        self.apply_btn.setEnabled(has_located)
        self.verify_btn.setEnabled(has_modified)
        self.save_btn.setEnabled(has_modified)
    
    def select_source_file(self):
        """选择源代码文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择源代码文件", "", 
            "Text Files (*.txt *.py *.c *.cpp *.h *.java);;All Files (*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    self.original_content = f.read()
                self.source_text.setPlainText(self.original_content)
                self.debug_text.append(f"已加载源代码文件: {file_path}")
                self.update_button_states()
            except Exception as e:
                QMessageBox.critical(self, "错误", f"读取源代码文件时出错: {str(e)}")
    
    def select_patch_file(self):
        """选择补丁文件"""
        file_path, _ = QFileDialog.getOpenFileName(
            self, "选择补丁文件", "", 
            "Patch Files (*.patch *.diff);;Text Files (*.txt);;All Files (*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'r', encoding='utf-8') as f:
                    self.patch_content = f.read()
                self.patch_text.setPlainText(self.patch_content)
                self.debug_text.append(f"已加载补丁文件: {file_path}")
                self.update_button_states()
            except Exception as e:
                QMessageBox.critical(self, "错误", f"读取补丁文件时出错: {str(e)}")
    
    def parse_patch(self):
        """解析补丁，增强块解析功能"""
        if not self.original_content or not self.patch_content:
            QMessageBox.warning(self, "警告", "请先选择源代码文件和补丁文件")
            return
        
        try:
            self.debug_text.append("\n=== 开始解析补丁 ===")
            self.parsed_hunks = self.parse_patch_content(self.patch_content)
            self.debug_text.append(f"解析完成，共找到 {len(self.parsed_hunks)} 个hunk")
            
            # 显示增强的解析结果
            for hunk_idx, hunk in enumerate(self.parsed_hunks):
                self.debug_text.append(f"\n===== Hunk {hunk_idx + 1} =====")
                self.debug_text.append(f"头部信息: {hunk['header']}")
                
                # 解析补丁块（增强部分）
                patch_blocks = self.analyze_patch_blocks(hunk['lines'])
                self.debug_text.append(f"包含 {len(patch_blocks)} 个补丁块:")
                
                for block_idx, block in enumerate(patch_blocks):
                    self.debug_text.append(f"\n--- 补丁块 {block_idx + 1} ---")
                    self.debug_text.append(f"块类型: {block['type']}")
                    
                    self.debug_text.append("前上下文:")
                    for line in block['pre_context']:
                        self.debug_text.append(f"  {line}")
                    
                    self.debug_text.append("块内容:")
                    for line in block['content']:
                        self.debug_text.append(f"  {line}")
                    
                    self.debug_text.append("后上下文:")
                    for line in block['post_context']:
                        self.debug_text.append(f"  {line}")
            
            self.update_button_states()
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"解析补丁时出错: {str(e)}")
            self.debug_text.append(f"错误: {str(e)}")
    
    def parse_patch_content(self, patch):
        """解析补丁内容，提取hunks"""
        hunks = []
        lines = patch.split('\n')
        i = 0
        
        while i < len(lines):
            line = lines[i]
            # 查找hunk起始标记
            if line.startswith('@@'):
                hunk = {
                    'header': line,
                    'lines': []
                }
                
                i += 1
                # 收集hunk内容直到下一个hunk开始或文件结束
                while i < len(lines) and not lines[i].startswith('@@'):
                    if lines[i]:  # 忽略空行
                        hunk['lines'].append(lines[i])
                    i += 1
                
                hunks.append(hunk)
            else:
                i += 1
        
        return hunks
    
    def analyze_patch_blocks(self, hunk_lines):
        """
        分析hunk中的补丁块，根据规则提取：
        - 块类型（增块、删块、改块）
        - 前上下文（块前的上下文行）
        - 块内容（连续的+/-标记行）
        - 后上下文（块后的上下文行）
        """
        # 第一步：标记所有行的类型
        tagged_lines = []
        for line in hunk_lines:
            if line.startswith('+'):
                tagged_lines.append({'type': 'add', 'content': line, 'original': line[1:]})
            elif line.startswith('-'):
                tagged_lines.append({'type': 'remove', 'content': line, 'original': line[1:]})
            elif line.startswith(' '):
                tagged_lines.append({'type': 'context', 'content': line, 'original': line[1:]})
            else:
                # 处理特殊行（如文件名行等）
                tagged_lines.append({'type': 'other', 'content': line, 'original': line})
        
        # 第二步：识别补丁块（连续的add/remove行）
        patch_blocks = []
        n = len(tagged_lines)
        i = 0
        
        while i < n:
            # 找到补丁块的起始（第一个add或remove）
            if tagged_lines[i]['type'] in ['add', 'remove']:
                block_start = i
                
                # 找到补丁块的结束（直到非add/remove行）
                while i < n and tagged_lines[i]['type'] in ['add', 'remove']:
                    i += 1
                block_end = i - 1
                
                # 提取块内容
                block_content = [tagged_lines[j]['content'] for j in range(block_start, block_end + 1)]
                
                # 确定块类型
                has_add = any(tagged_lines[j]['type'] == 'add' for j in range(block_start, block_end + 1))
                has_remove = any(tagged_lines[j]['type'] == 'remove' for j in range(block_start, block_end + 1))
                
                if has_add and has_remove:
                    block_type = '改块'  # 既有增又有删
                elif has_add:
                    block_type = '增块'  # 只有增加
                else:
                    block_type = '删块'  # 只有删除
                
                # 提取前上下文：从块前一行向前到上一个补丁块或hunk开头
                pre_context = []
                j = block_start - 1
                while j >= 0 and tagged_lines[j]['type'] == 'context':
                    pre_context.insert(0, tagged_lines[j]['original'])  # 逆序插入，保持原始顺序
                    j -= 1
                
                # 提取后上下文：从块后一行向后到下一个补丁块或hunk结尾
                post_context = []
                j = block_end + 1
                while j < n and tagged_lines[j]['type'] == 'context':
                    post_context.append(tagged_lines[j]['original'])
                    j += 1
                
                # 添加到补丁块列表
                patch_blocks.append({
                    'type': block_type,
                    'pre_context': pre_context,
                    'content': block_content,
                    'post_context': post_context,
                    'start_idx': block_start,
                    'end_idx': block_end
                })
            else:
                i += 1
        
        return patch_blocks
    
    # 保持其他方法（locate_patch、find_hunk_position、apply_patch等）不变...
    
    def locate_patch(self):
        """定位补丁块，而不是整个hunk"""
        if not self.parsed_hunks:
            QMessageBox.warning(self, "警告", "请先解析补丁")
            return
        
        try:
            self.debug_text.append("\n=== 开始定位补丁块 ===")
            lines = self.original_content.split('\n')
            self.located_positions = []  # 现在存储的是每个补丁块的位置
            
            for hunk_index, hunk in enumerate(self.parsed_hunks):
                self.debug_text.append(f"\n定位Hunk {hunk_index + 1}中的补丁块:")
                
                # 解析hunk中的补丁块
                patch_blocks = self.analyze_patch_blocks(hunk['lines'])
                
                for block_index, block in enumerate(patch_blocks):
                    self.debug_text.append(f"\n--- 定位补丁块 {block_index + 1} ({block['type']}) ---")
                    
                    # 定位单个补丁块
                    position = self.find_block_position(lines, block, hunk_index, block_index)
                    
                    if position is None:
                        self.debug_text.append(f"  补丁块 {block_index + 1} 无法找到匹配位置")
                        self.located_positions.append(None)
                    else:
                        self.debug_text.append(f"  补丁块 {block_index + 1} 在位置 {position} 找到匹配")
                        self.located_positions.append({
                            'hunk_index': hunk_index,
                            'block_index': block_index,
                            'position': position,
                            'block': block
                        })
            
            success_count = sum(1 for pos in self.located_positions if pos is not None)
            self.debug_text.append(f"\n定位完成: {success_count}/{len(self.located_positions)} 个补丁块定位成功")
            self.update_button_states()
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"定位补丁块时出错: {str(e)}")
            self.debug_text.append(f"错误: {str(e)}")

    def find_block_position(self, lines, block, hunk_index, block_index):
        """定位单个补丁块的位置，按照从近到远的顺序匹配"""
        block_type = block['type']
        pre_context = block['pre_context']
        post_context = block['post_context']
        block_content = block['content']
        
        self.debug_text.append(f"  定位{block_type}:")
        self.debug_text.append(f"  前上下文: {len(pre_context)} 行")
        self.debug_text.append(f"  后上下文: {len(post_context)} 行")
        self.debug_text.append(f"  块内容: {len(block_content)} 行")
        
        if block_type == '增块':
            return self.locate_add_block(lines, pre_context, post_context, block_content)
        elif block_type == '删块':
            return self.locate_remove_block(lines, pre_context, post_context, block_content)
        elif block_type == '改块':
            return self.locate_modify_block(lines, pre_context, post_context, block_content)
        else:
            self.debug_text.append(f"  未知块类型: {block_type}")
            return None
        
    # 新增：行标准化方法（根据选项处理行首空格/Tab）
    def normalize_line(self, line):
        """
        根据match_leading_whitespace状态处理行：
        - 勾选：保留行首空格/Tab（精确匹配）
        - 取消：去除行首空格/Tab（忽略空白匹配）
        """
        if not self.match_leading_whitespace:
            return line.lstrip('\t ')  # 仅去除行首的Tab和空格
        return line


    def locate_add_block(self, lines, pre_context, post_context, block_content):
        """定位增块，采用迭代匹配算法"""
        self.debug_text.append("  === 开始定位增块 (迭代算法) ===")
        
        # 记录调试信息
        self.debug_text.append(f"  前上下文 ({len(pre_context)} 行):")
        for i, line in enumerate(pre_context):
            self.debug_text.append(f"    [{i}]: '{line}'")
        self.debug_text.append(f"  后上下文 ({len(post_context)} 行):")
        for i, line in enumerate(post_context):
            self.debug_text.append(f"    [{i}]: '{line}'")
        
        # 如果没有上下文信息，无法定位
        if not pre_context and not post_context:
            self.debug_text.append("  错误: 没有上下文信息，无法定位增块")
            return None
        
        # 初始化候选位置和上下文片段
        candidates = None
        fragments = None
        
        # 确定匹配顺序: pre_context[-1], post_context[0], pre_context[-2], post_context[1], ...
        match_order = []
        max_depth = max(len(pre_context), len(post_context))
        
        for i in range(max_depth):
            if i < len(pre_context):
                match_order.append(('pre', len(pre_context) - 1 - i, pre_context[len(pre_context) - 1 - i]))
            if i < len(post_context):
                match_order.append(('post', i, post_context[i]))
        
        self.debug_text.append(f"  匹配顺序: {[(t, idx, line) for t, idx, line in match_order]}")
        
        # 迭代匹配过程
        for step, (context_type, idx, context_line) in enumerate(match_order):
            self.debug_text.append(f"  第 {step+1} 步: 使用{context_type}上下文[{idx}] '{context_line}'")
            
            # 使用统一的匹配函数
            result, candidates, fragments = self.match_context1(
                lines, context_type, idx, context_line, candidates, fragments,
                len(pre_context), len(post_context)
            )
            
            if result == -1:  # 匹配失败
                self.debug_text.append(f"  第 {step+1} 步匹配失败: 没有找到匹配行")
                return None
            elif result == 0:  # 唯一匹配
                self.debug_text.append(f"  第 {step+1} 步找到唯一匹配: {candidates[0]}")
                return candidates[0]
            else:  # 多处匹配
                self.debug_text.append(f"  第 {step+1} 步找到 {len(candidates)} 个候选位置")
        
        # 如果所有步骤都执行完毕仍有多个候选
        if candidates and len(candidates) > 1:
            self.debug_text.append(f"  仍有 {len(candidates)} 个候选位置: {candidates}")
            return None
        elif candidates and len(candidates) == 1:
            self.debug_text.append(f"  迭代结束后找到唯一匹配: {candidates[0]}")
            return candidates[0]
        else:
            self.debug_text.append("  迭代结束后没有候选位置")
            return None
        
    def match_context1(self, lines, context_type, idx, context_line, candidates=None, fragments=None, pre_len=0, post_len=0):
        """
        统一的上下文匹配函数
        
        参数:
        - lines: 原始文本行列表
        - context_type: 上下文类型 ('pre' 或 'post')
        - idx: 上下文行在原始上下文数组中的索引
        - context_line: 要匹配的上下文行
        - candidates: 候选位置列表 (如果是第一步匹配则为None)
        - fragments: 候选位置的上下文片段列表 (如果是第一步匹配则为None)
        - pre_len: 前上下文的长度
        - post_len: 后上下文的长度
        
        返回:
        - 匹配结果 (-1: 失败, 0: 唯一匹配, 1: 多处匹配)
        - 新的候选位置列表
        - 新的上下文片段列表
        """
        new_candidates = []
        new_fragments = []
        
        # 第一步匹配: 在整个文本中查找匹配行
        if candidates is None:
            for i, line in enumerate(lines):
                # 行匹配前先标准化（关键修改）
                if self.normalize_line(line) == self.normalize_line(context_line):
                    new_candidates.append(i)
                    
                    # 创建上下文片段
                    if context_type == 'pre':
                        # 对于前上下文，我们需要检查前面的行
                        fragment = {
                            'pre': lines[max(0, i - pre_len + 1):i+1] if pre_len > 0 else [],
                            'post': lines[i+1:min(len(lines), i+1+post_len)] if post_len > 0 else []
                        }
                    else:  # context_type == 'post'
                        # 对于后上下文，我们需要检查后面的行
                        fragment = {
                            'pre': lines[max(0, i - pre_len):i] if pre_len > 0 else [],
                            'post': lines[i:min(len(lines), i+post_len)] if post_len > 0 else []
                        }
                    
                    new_fragments.append(fragment)
        
        # 迭代匹配: 在现有候选位置中过滤
        else:
            for candidate_pos, fragment in zip(candidates, fragments):
                # 检查上下文片段中对应位置的行是否匹配
                if context_type == 'pre':
                    # 对于前上下文，检查片段中对应位置的行
                    fragment_idx = pre_len - 1 - idx  # 计算在片段中的索引
                    if 0 <= fragment_idx < len(fragment['pre']) and self.normalize_line(fragment['pre'][fragment_idx]) == self.normalize_line(context_line):
                        # 行匹配前先标准化（关键修改）                       
                        new_candidates.append(candidate_pos)
                        new_fragments.append(fragment)
                else:  # context_type == 'post'
                    # 对于后上下文，检查片段中对应位置的行
                    fragment_idx = idx  # 计算在片段中的索引
                    if 0 <= fragment_idx < len(fragment['post']) and self.normalize_line(fragment['post'][fragment_idx]) == self.normalize_line(context_line):
                        # 行匹配前先标准化（关键修改）                       
                        new_candidates.append(candidate_pos)
                        new_fragments.append(fragment)
        
        # 确定匹配结果
        if not new_candidates:
            return -1, None, None
        elif len(new_candidates) == 1:
            return 0, new_candidates, new_fragments
        else:
            return 1, new_candidates, new_fragments    

    def locate_remove_block(self, lines, pre_context, post_context, block_content):
        """定位删块，按照从近到远逐行匹配"""
        self.debug_text.append("  === 开始定位删块 ===")
        
        # 提取要删除的行（去掉'-'前缀）
        remove_lines = [line[1:] for line in block_content if line.startswith('-')]
        
        if not remove_lines:
            self.debug_text.append("  没有要删除的行")
            return None
        
        self.debug_text.append("  尝试匹配要删除的行:")
        for i, line in enumerate(remove_lines):
            self.debug_text.append(f"    删除行[{i}]: '{line}'")
        
        # 首先尝试匹配第一行删除行
        first_remove_line = remove_lines[0] if remove_lines else ""
        candidates = []
        
        for i in range(len(lines)):
            # 行匹配前先标准化（关键修改）
            if self.normalize_line(lines[i]) == self.normalize_line(first_remove_line):
                candidates.append(i)
                self.debug_text.append(f"    在位置 {i} 找到删除行匹配: '{first_remove_line}'")
        
        if len(candidates) == 0:
            self.debug_text.append("  没有找到删除行匹配")
            return None
        elif len(candidates) == 1:
            self.debug_text.append(f"  通过删除行找到唯一位置: {candidates[0]}")
            return candidates[0]
        else:
            self.debug_text.append(f"  找到 {len(candidates)} 个候选位置: {candidates}")
            
            # 如果有多个匹配，尝试使用更多删除行来缩小范围
            if len(remove_lines) > 1:
                self.debug_text.append("  尝试使用更多删除行缩小范围")
                filtered_candidates = []
                
                for pos in candidates:
                    match = True
                    for j in range(1, min(len(remove_lines), len(lines) - pos)):
                        # 行匹配前先标准化（关键修改）
                        if self.normalize_line(lines[pos + j]) != self.normalize_line(remove_lines[j]):
                            match = False
                            self.debug_text.append(f"    位置 {pos} 的第 {j} 行不匹配")
                            break
                    
                    if match:
                        filtered_candidates.append(pos)
                        self.debug_text.append(f"    位置 {pos} 通过多行删除行检查")
                
                candidates = filtered_candidates
                
                if len(candidates) == 1:
                    self.debug_text.append(f"  通过多行删除行找到唯一位置: {candidates[0]}")
                    return candidates[0]
                elif len(candidates) > 1:
                    self.debug_text.append(f"  仍有 {len(candidates)} 个候选位置: {candidates}")
                else:
                    self.debug_text.append("  没有候选位置通过多行删除行检查")
            
            # 如果还有多个匹配，尝试使用上下文缩小范围
            if len(candidates) > 1:
                self.debug_text.append("  尝试使用上下文缩小范围")
                return self.gradual_narrow_with_context(lines, pre_context, post_context, candidates)
        
        self.debug_text.append("  无法找到匹配位置")
        return None
    
    def gradual_narrow_with_context(self, lines, pre_context, post_context, candidates):
        """逐步使用上下文缩小候选范围"""
        self.debug_text.append("  === 逐步使用上下文缩小范围 ===")
        
        # 首先尝试使用前上下文的最后一行
        if pre_context:
            last_pre = pre_context[-1]
            self.debug_text.append(f"  尝试使用前上下文最后一行: '{last_pre}'")
            
            filtered_candidates = []
            for pos in candidates:
                if pos > 0 and self.normalize_line(lines[pos-1]) == self.normalize_line(last_pre):   # 行匹配前先标准化（关键修改）                                       
                    filtered_candidates.append(pos)
                    self.debug_text.append(f"    位置 {pos} 匹配前上下文最后一行")
                else:
                    self.debug_text.append(f"    位置 {pos} 不匹配前上下文最后一行")
            
            candidates = filtered_candidates
            
            if len(candidates) == 1:
                self.debug_text.append(f"  使用前上下文最后一行找到唯一位置: {candidates[0]}")
                return candidates[0]
            elif not candidates:
                self.debug_text.append("  没有候选位置匹配前上下文最后一行")
                return None
        
        # 然后尝试使用后上下文的第一行
        if post_context:
            first_post = post_context[0]
            self.debug_text.append(f"  尝试使用后上下文第一行: '{first_post}'")
            
            filtered_candidates = []
            for pos in candidates:
                if pos + 1 < len(lines) and self.normalize_line(lines[pos + 1]) == self.normalize_line(first_post):  # 行匹配前先标准化（关键修改）
                    filtered_candidates.append(pos)
                    self.debug_text.append(f"    位置 {pos} 匹配后上下文第一行")
                else:
                    self.debug_text.append(f"    位置 {pos} 不匹配后上下文第一行")
            
            candidates = filtered_candidates
            
            if len(candidates) == 1:
                self.debug_text.append(f"  使用后上下文第一行找到唯一位置: {candidates[0]}")
                return candidates[0]
            elif not candidates:
                self.debug_text.append("  没有候选位置匹配后上下文第一行")
                return None
        
        # 逐步增加前上下文行数（从后往前）
        if pre_context:
            for i in range(len(pre_context)-2, -1, -1):  # 从倒数第二行开始向前
                line = pre_context[i]
                self.debug_text.append(f"  尝试使用前上下文[{i}]: '{line}'")
                
                filtered_candidates = []
                for pos in candidates:
                    if pos - (len(pre_context) - i) >= 0 and self.normalize_line(lines[pos - (len(pre_context) - i)]) == self.normalize_line(line): # 行匹配前先标准化（关键修改）
                        filtered_candidates.append(pos)
                        self.debug_text.append(f"    位置 {pos} 匹配前上下文[{i}]")
                    else:
                        self.debug_text.append(f"    位置 {pos} 不匹配前上下文[{i}]")
                
                candidates = filtered_candidates
                
                if len(candidates) == 1:
                    self.debug_text.append(f"  使用前上下文[{i}]找到唯一位置: {candidates[0]}")
                    return candidates[0]
                elif not candidates:
                    self.debug_text.append(f"  没有候选位置匹配前上下文[{i}]")
                    return None
        
        # 逐步增加后上下文行数（从前往后）
        if post_context:
            for i in range(1, len(post_context)):  # 从第二行开始向后
                line = post_context[i]
                self.debug_text.append(f"  尝试使用后上下文[{i}]: '{line}'")
                
                filtered_candidates = []
                for pos in candidates:
                    if pos + 1 + i < len(lines) and self.normalize_line(lines[pos + 1 + i]) == self.normalize_line(line): # 行匹配前先标准化（关键修改）
                        filtered_candidates.append(pos)
                        self.debug_text.append(f"    位置 {pos} 匹配后上下文[{i}]")
                    else:
                        self.debug_text.append(f"    位置 {pos} 不匹配后上下文[{i}]")
                
                candidates = filtered_candidates
                
                if len(candidates) == 1:
                    self.debug_text.append(f"  使用后上下文[{i}]找到唯一位置: {candidates[0]}")
                    return candidates[0]
                elif not candidates:
                    self.debug_text.append(f"  没有候选位置匹配后上下文[{i}]")
                    return None
        
        if len(candidates) > 1:
            self.debug_text.append(f"  仍有 {len(candidates)} 个候选位置: {candidates}")
            return None
        else:
            self.debug_text.append("  没有候选位置匹配")
            return None

    def locate_modify_block(self, lines, pre_context, post_context, block_content):
        """定位改块，按照从近到远逐行匹配"""
        self.debug_text.append("  === 开始定位改块 ===")
        
        # 提取要删除的行（去掉'-'前缀）
        remove_lines = [line[1:] for line in block_content if line.startswith('-')]
        
        if not remove_lines:
            self.debug_text.append("  没有要删除的行")
            return None
        
        # 使用与删块相同的定位逻辑
        return self.locate_remove_block(lines, pre_context, post_context, block_content)

    def narrow_with_context(self, lines, pre_context, post_context, candidates):
        """使用上下文缩小候选范围，详细打印过程"""
        self.debug_text.append("  === 使用上下文缩小范围 ===")
        
        pre_len = len(pre_context)
        post_len = len(post_context)
        
        self.debug_text.append("  尝试使用上下文缩小范围:")
        if pre_context:
            for i, line in enumerate(pre_context):
                self.debug_text.append(f"    前上下文[{i}]: '{line}'")
        if post_context:
            for i, line in enumerate(post_context):
                self.debug_text.append(f"    后上下文[{i}]: '{line}'")
        
        filtered_candidates = []
        for pos in candidates:
            self.debug_text.append(f"  检查候选位置 {pos}:")
            
            # 检查前上下文匹配
            pre_match = True
            if pre_len > 0:
                if pos < pre_len:
                    pre_match = False
                    self.debug_text.append(f"    位置 {pos} 不足以前上下文长度 {pre_len}")
                else:
                    for j in range(pre_len):
                        expected = pre_context[j]
                        actual = lines[pos - pre_len + j]
                        if actual != expected:
                            # 行匹配前先标准化（关键修改）
                            if self.normalize_line(actual) != self.normalize_line(expected):
                                pre_match = False
                                self.debug_text.append(f"    前上下文[{j}] 不匹配: 期望 '{expected}', 实际 '{actual}'")
                                break
                        else:
                            self.debug_text.append(f"    前上下文[{j}] 匹配: '{expected}'")
            
            # 检查后上下文匹配
            post_match = True
            if post_len > 0:
                if pos + post_len > len(lines):
                    post_match = False
                    self.debug_text.append(f"    位置 {pos} 加上后上下文长度 {post_len} 超出文件范围")
                else:
                    for j in range(post_len):
                        expected = post_context[j]
                        actual = lines[pos + j]
                        if actual != expected:
                            # 行匹配前先标准化（关键修改）
                            if self.normalize_line(actual) != self.normalize_line(expected):
                                post_match = False
                                self.debug_text.append(f"    后上下文[{j}] 不匹配: 期望 '{expected}', 实际 '{actual}'")
                                break
                        else:
                            self.debug_text.append(f"    后上下文[{j}] 匹配: '{expected}'")
            
            if pre_match and post_match:
                filtered_candidates.append(pos)
                self.debug_text.append(f"    位置 {pos} 通过上下文检查")
            else:
                self.debug_text.append(f"    位置 {pos} 未通过上下文检查")
        
        if len(filtered_candidates) == 1:
            self.debug_text.append(f"  使用上下文缩小到唯一位置: {filtered_candidates[0]}")
            return filtered_candidates[0]
        elif len(filtered_candidates) > 1:
            self.debug_text.append(f"  仍有 {len(filtered_candidates)} 个候选位置: {filtered_candidates}")
            return None
        else:
            self.debug_text.append("  没有候选位置匹配上下文")
            return None
    
    def find_hunk_position(self, lines, hunk, hunk_index):
        # 原有定位逻辑保持不变
        context_before = []
        context_after = []
        changes = []
        
        for line in hunk['lines']:
            if line.startswith(' '):
                context_before.append(line[1:])
                context_after.append(line[1:])
            elif line.startswith('-'):
                changes.append(('remove', line[1:]))
                context_before.append(line[1:])
            elif line.startswith('+'):
                changes.append(('add', line[1:]))
                context_after.append(line[1:])
        
        self.debug_text.append(f"  上下文行: 前{len(context_before)}行, 后{len(context_after)}行")
        self.debug_text.append(f"  变更操作: {len(changes)}个")
        
        if not changes:
            header_match = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@', hunk['header'])
            if header_match:
                old_start = int(header_match.group(1))
                return max(0, old_start - 1)
            return 0
        
        header_match = re.match(r'@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@', hunk['header'])
        if header_match:
            old_start = int(header_match.group(1))
            start_line = max(0, old_start - 1 - 3)
            end_line = min(len(lines), old_start - 1 + 3)
            
            self.debug_text.append(f"  使用行号提示: {old_start}, 搜索范围: {start_line}-{end_line}")
            
            for i in range(start_line, end_line):
                if self.match_context(lines, i, context_before):
                    self.debug_text.append(f"    在位置 {i} 找到精确匹配")
                    return i
        
        for distance in range(1, 10):
            start_line = max(0, (old_start - 1 if 'old_start' in locals() else 0) - distance)
            end_line = min(len(lines), (old_start - 1 if 'old_start' in locals() else 0) + distance)
            
            self.debug_text.append(f"  扩大搜索范围到: {start_line}-{end_line} (距离: {distance})")
            
            for i in range(start_line, end_line):
                if self.match_context(lines, i, context_before):
                    self.debug_text.append(f"    在位置 {i} 找到模糊匹配")
                    return i
        
        for distance in range(1, 20):
            start_line = max(0, (old_start - 1 if 'old_start' in locals() else 0) - distance)
            end_line = min(len(lines), (old_start - 1 if 'old_start' in locals() else 0) + distance)
            
            best_match = None
            best_score = 0
            
            for i in range(start_line, end_line):
                score = self.partial_match_score(lines, i, context_before)
                if score > best_score:
                    best_score = score
                    best_match = i
            
            if best_match is not None and best_score >= len(context_before) * 0.7:
                self.debug_text.append(f"    在位置 {best_match} 找到部分匹配 (匹配度: {best_score/len(context_before)*100:.1f}%)")
                return best_match
        
        self.debug_text.append("  无法找到匹配位置")
        return None
    
    def apply_patch(self):
        """应用补丁块，完全按照定位的位置进行，不考虑行号变化"""
        if not self.located_positions or None in self.located_positions:
            QMessageBox.warning(self, "警告", "请先成功定位所有补丁块")
            return
        
        try:
            self.debug_text.append("\n=== 开始应用补丁块 ===")
            lines = self.original_content.split('\n')
            
            # 按位置排序补丁块，从大到小（从尾部向头部）
            sorted_positions = sorted(
                [pos for pos in self.located_positions if pos is not None],
                key=lambda x: x['position'],
                reverse=True
            )
            
            for pos_info in sorted_positions:
                hunk_index = pos_info['hunk_index']
                block_index = pos_info['block_index']
                position = pos_info['position']
                block = pos_info['block']
                
                self.debug_text.append(f"\n应用Hunk {hunk_index + 1} 的补丁块 {block_index + 1} 到位置 {position}:")
                
                # 应用补丁块，不考虑行号变化
                lines = self.apply_block_without_offset(lines, block, position)
            
            self.modified_content = '\n'.join(lines)
            self.modified_text.setPlainText(self.modified_content)
            
            self.debug_text.append("\n补丁应用完成")
            
            if self.verify_checkbox.isChecked():
                self.verify_patch()
                
            self.update_button_states()
            
        except Exception as e:
            QMessageBox.critical(self, "错误", f"应用补丁块时出错: {str(e)}")
            self.debug_text.append(f"错误: {str(e)}")

    def apply_block_without_offset(self, lines, block, pos):
        """应用单个补丁块，不考虑行号变化"""
        new_lines = lines[:]
        
        # 根据块类型应用不同的操作
        if block['type'] == '增块':
            # 在指定位置插入添加的行
            insert_pos = pos
            for line in block['content']:
                if line.startswith('+'):
                    new_lines.insert(insert_pos, line[1:])
                    self.debug_text.append(f"  添加行: '{line[1:]}'")
                    insert_pos += 1
        
        elif block['type'] == '删块':
            # 删除指定的行
            delete_pos = pos
            for line in block['content']:
                if line.startswith('-'):
                    if delete_pos < len(new_lines) and new_lines[delete_pos] == line[1:]:
                        removed_line = new_lines.pop(delete_pos)
                        self.debug_text.append(f"  删除行: '{removed_line}'")
                    else:
                        self.debug_text.append(f"  警告: 无法找到要删除的行 '{line[1:]}'")
        
        elif block['type'] == '改块':
            # 先删除后添加
            delete_pos = pos
            for line in block['content']:
                if line.startswith('-'):
                    if delete_pos < len(new_lines) and new_lines[delete_pos] == line[1:]:
                        removed_line = new_lines.pop(delete_pos)
                        self.debug_text.append(f"  删除行: '{removed_line}'")
                    else:
                        self.debug_text.append(f"  警告: 无法找到要删除的行 '{line[1:]}'")
            
            # 在删除的位置添加新行
            insert_pos = delete_pos
            for line in block['content']:
                if line.startswith('+'):
                    new_lines.insert(insert_pos, line[1:])
                    self.debug_text.append(f"  添加行: '{line[1:]}'")
                    insert_pos += 1
        
        return new_lines

    def apply_block(self, lines, block, pos):
        """应用单个补丁块"""
        new_lines = lines[:]
        
        # 根据块类型应用不同的操作
        if block['type'] == '增块':
            # 在指定位置插入添加的行
            insert_pos = pos + len(block['pre_context']) if block['pre_context'] else pos
            for line in block['content']:
                if line.startswith('+'):
                    new_lines.insert(insert_pos, line[1:])
                    self.debug_text.append(f"  添加行: '{line[1:]}'")
                    insert_pos += 1
        
        elif block['type'] == '删块':
            # 删除指定的行
            delete_pos = pos + len(block['pre_context']) if block['pre_context'] else pos
            for line in block['content']:
                if line.startswith('-'):
                    if delete_pos < len(new_lines) and new_lines[delete_pos] == line[1:]:
                        removed_line = new_lines.pop(delete_pos)
                        self.debug_text.append(f"  删除行: '{removed_line}'")
                    else:
                        self.debug_text.append(f"  警告: 无法找到要删除的行 '{line[1:]}'")
        
        elif block['type'] == '改块':
            # 先删除后添加
            delete_pos = pos + len(block['pre_context']) if block['pre_context'] else pos
            for line in block['content']:
                if line.startswith('-'):
                    if delete_pos < len(new_lines) and new_lines[delete_pos] == line[1:]:
                        removed_line = new_lines.pop(delete_pos)
                        self.debug_text.append(f"  删除行: '{removed_line}'")
                    else:
                        self.debug_text.append(f"  警告: 无法找到要删除的行 '{line[1:]}'")
            
            # 在删除的位置添加新行
            insert_pos = delete_pos
            for line in block['content']:
                if line.startswith('+'):
                    new_lines.insert(insert_pos, line[1:])
                    self.debug_text.append(f"  添加行: '{line[1:]}'")
                    insert_pos += 1
        
        return new_lines
    
    # 保持match_context、partial_match_score、apply_hunk、verify_patch、save_changes等方法不变...

    def save_changes(self):
        """保存修改后的内容到文件"""
        if not self.modified_content:
            QMessageBox.warning(self, "警告", "没有可保存的修改")
            return
        
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存修改后的文件", "", 
            "Text Files (*.txt *.py *.c *.cpp *.h *.java);;All Files (*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(self.modified_content)
                self.debug_text.append(f"已保存修改到: {file_path}")
                QMessageBox.information(self, "成功", "文件已保存")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存文件时出错: {str(e)}")
                self.debug_text.append(f"保存错误: {str(e)}")
    
    def match_context(self, lines, start, context):
        if start + len(context) > len(lines):
            return False
        
        for i in range(len(context)):
            # 行匹配前先标准化（关键修改）
            if self.normalize_line(lines[start + i]) != self.normalize_line(context[i]):
                return False
        
        return True
    
    def partial_match_score(self, lines, start, context):
        if start + len(context) > len(lines):
            return 0
        
        score = 0
        for i in range(len(context)):
            # 行匹配前先标准化（关键修改）
            if self.normalize_line(lines[start + i]) == self.normalize_line(context[i]):
                score += 1
        
        return score
    
    def apply_hunk(self, lines, hunk, pos):
        operations = []
        for line in hunk['lines']:
            if line.startswith(' '):
                operations.append(('keep', line[1:]))
            elif line.startswith('-'):
                operations.append(('remove', line[1:]))
            elif line.startswith('+'):
                operations.append(('add', line[1:]))
        
        new_lines = lines[:pos]
        op_index = 0
        line_index = pos
        
        while op_index < len(operations):
            op, content = operations[op_index]
            
            if op == 'keep':
                if line_index < len(lines) and lines[line_index] == content:
                    new_lines.append(lines[line_index])
                    self.debug_text.append(f"  保留行 {line_index+1}: '{lines[line_index]}'")
                    line_index += 1
                else:
                    self.debug_text.append(f"  警告: 上下文不匹配，期望 '{content}'，找到 '{lines[line_index] if line_index < len(lines) else 'EOF'}'")
                    found = False
                    for i in range(line_index, min(line_index + 5, len(lines))):
                        if lines[i] == content:
                            for j in range(line_index, i):
                                self.debug_text.append(f"  跳过行 {j+1}: '{lines[j]}'")
                            line_index = i
                            new_lines.append(lines[line_index])
                            self.debug_text.append(f"  保留行 {line_index+1}: '{lines[line_index]}'")
                            line_index += 1
                            found = True
                            break
                    
                    if not found:
                        self.debug_text.append(f"  错误: 无法找到匹配的上下文行 '{content}'")
                        new_lines.append(content)
            elif op == 'remove':
                if line_index < len(lines) and lines[line_index] == content:
                    self.debug_text.append(f"  删除行 {line_index+1}: '{lines[line_index]}'")
                    line_index += 1
                else:
                    found = False
                    for i in range(line_index, min(line_index + 5, len(lines))):
                        if lines[i] == content:
                            self.debug_text.append(f"  删除行 {i+1}: '{lines[i]}' (跳过 {i - line_index} 行)")
                            line_index = i + 1
                            found = True
                            break
                    
                    if not found:
                        self.debug_text.append(f"  警告: 无法找到要删除的行 '{content}'")
            elif op == 'add':
                new_lines.append(content)
                self.debug_text.append(f"  添加行: '{content}'")
            
            op_index += 1
        
        if line_index < len(lines):
            new_lines.extend(lines[line_index:])
        
        return new_lines
    
    def verify_patch(self):
        if not self.modified_content:
            QMessageBox.warning(self, "警告", "请先应用补丁")
            return
        
        self.debug_text.append("\n=== 开始验证补丁 ===")
        original_lines = self.original_content.split('\n')
        modified_lines = self.modified_content.split('\n')
        
        # 验证每个补丁块的应用
        success_count = 0
        total_count = 0
        
        for pos_info in self.located_positions:
            if pos_info is None:
                continue
                
            total_count += 1
            hunk_index = pos_info['hunk_index']
            block_index = pos_info['block_index']
            block = pos_info['block']
            position = pos_info['position']
            
            # 根据块类型验证
            if block['type'] == '增块':
                # 检查添加的行是否存在
                added_lines = [line[1:] for line in block['content'] if line.startswith('+')]
                all_found = all(line in modified_lines for line in added_lines)
                if all_found:
                    success_count += 1
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (增块) 验证成功: 所有 {len(added_lines)} 个添加行已正确应用")
                else:
                    # 找出缺失的行
                    missing_lines = [line for line in added_lines if line not in modified_lines]
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (增块) 验证失败:")
                    self.debug_text.append(f"  期望添加的行: {added_lines}")
                    self.debug_text.append(f"  缺失的行: {missing_lines}")
            
            elif block['type'] == '删块':
                # 检查删除的行是否不存在
                removed_lines = [line[1:] for line in block['content'] if line.startswith('-')]
                all_removed = all(line not in modified_lines for line in removed_lines)
                if all_removed:
                    success_count += 1
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (删块) 验证成功: 所有 {len(removed_lines)} 个删除行已正确移除")
                else:
                    # 找出仍然存在的行
                    remaining_lines = [line for line in removed_lines if line in modified_lines]
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (删块) 验证失败:")
                    self.debug_text.append(f"  期望删除的行: {removed_lines}")
                    self.debug_text.append(f"  仍然存在的行: {remaining_lines}")
            
            elif block['type'] == '改块':
                # 检查删除的行是否不存在，添加的行是否存在
                removed_lines = [line[1:] for line in block['content'] if line.startswith('-')]
                added_lines = [line[1:] for line in block['content'] if line.startswith('+')]
                
                removed_ok = all(line not in modified_lines for line in removed_lines)
                added_ok = all(line in modified_lines for line in added_lines)
                
                if removed_ok and added_ok:
                    success_count += 1
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (改块) 验证成功: 所有 {len(removed_lines)} 个删除行和 {len(added_lines)} 个添加行已正确应用")
                else:
                    self.debug_text.append(f"Hunk {hunk_index + 1} 的补丁块 {block_index + 1} (改块) 验证失败:")
                    if not removed_ok:
                        remaining_lines = [line for line in removed_lines if line in modified_lines]
                        self.debug_text.append(f"  期望删除的行: {removed_lines}")
                        self.debug_text.append(f"  仍然存在的行: {remaining_lines}")
                    if not added_ok:
                        missing_lines = [line for line in added_lines if line not in modified_lines]
                        self.debug_text.append(f"  期望添加的行: {added_lines}")
                        self.debug_text.append(f"  缺失的行: {missing_lines}")
        
        if success_count == total_count:
            self.debug_text.append(f"补丁验证成功: 所有 {success_count} 个补丁块已正确应用")
            QMessageBox.information(self, "成功", "补丁验证成功")
        else:
            self.debug_text.append(f"补丁验证失败: {success_count}/{total_count} 个补丁块正确应用")
            QMessageBox.warning(self, "验证失败", f"补丁验证失败: {success_count}/{total_count} 个补丁块正确应用")

    def generate_diff(self):
        """生成修改后文件与原始文件的差异对比"""
        if not self.modified_content:
            QMessageBox.warning(self, "警告", "没有可比较的修改")
            return
        
        try:
            # 使用difflib生成差异
            original_lines = self.original_content.splitlines(keepends=True)
            modified_lines = self.modified_content.splitlines(keepends=True)
            
            # 生成统一的diff格式
            diff = difflib.unified_diff(
                original_lines, 
                modified_lines,
                fromfile='原始文件',
                tofile='修改后文件'
            )
            
            # 将生成器转换为字符串
            diff_output = ''.join(diff)
            
            # 显示差异结果
            if not diff_output:
                self.debug_text.append("\n=== 差异对比 ===\n没有差异")
            else:
                self.debug_text.append("\n=== 差异对比 ===")
                self.debug_text.append(diff_output)
                
                # 提供保存差异文件的功能
                self.save_diff_file(diff_output)
                
        except Exception as e:
            QMessageBox.critical(self, "错误", f"生成差异对比时出错: {str(e)}")
            self.debug_text.append(f"生成差异错误: {str(e)}")

    def save_diff_file(self, diff_content):
        """保存差异文件"""
        file_path, _ = QFileDialog.getSaveFileName(
            self, "保存差异文件", "", 
            "Diff Files (*.diff *.patch);;Text Files (*.txt);;All Files (*)"
        )
        
        if file_path:
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    f.write(diff_content)
                self.debug_text.append(f"已保存差异文件到: {file_path}")
                QMessageBox.information(self, "成功", "差异文件已保存")
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存差异文件时出错: {str(e)}")
                self.debug_text.append(f"保存差异文件错误: {str(e)}")

def main():
    app = QApplication(sys.argv)
    window = PatchApplicator()
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()


